CloudFront Hosting ToolkitをAWS CDKのL3 Constructとして使ってみた

CloudFront Hosting ToolkitをAWS CDKのL3 Constructとして使ってみた

5分ほどでフロントエンドのホスティングとCI/CDパイプラインを用意することができる
Clock Icon2024.07.03

CloudFront Hosting ToolkitをAWS CDKのL3 Constructとして使用したい

こんにちは、のんピ(@non____97)です。

皆さんはCloudFront Hosting ToolkitをAWS CDKのL3 Constructとして使用したいなと思ったことはありますか? 私はあります。

先日、CloudFront Hosting Toolkitという、CLIまたはAWS CDKでフロントエンドのホスティングとCI/CDパイプラインを用意するツールを紹介しました。詳細は以下記事をご覧ください。

https://dev.classmethod.jp/articles/cloudfront-hosting-toolkit/

こちらのツールはAWS CDKのL3 Constructとして使用することも可能です。L3 Constructとして使用することで、周辺環境もまとめてデプロイすることが可能です。

実際に試してみたので紹介します。

いきなりまとめ

  • AWS CDKのL3 Constructとして使用すれば、5分ほどでフロントエンドのホスティングとCI/CDパイプラインを用意することができる
  • CloudFront Hosting ToolkitをAWS CDKのL3 Constructとして使用する際は以下に注意
    • Lambda Layerで使用するAWS CRTのコードが正しくバンドルされるように、別途Lambda Layerで使用するパッケージを定義しているディレクトリでnpm ciを叩く必要がある
    • ドメイン名やRoute 53 Hosted ZoneのIDを指定するだけでは、カスタムドメインの設定はされない
      • 手動でACMで発行した証明書のARNを指定する必要がある
      • ALIASレコードの設定も手動で行う必要がある

やってみた

デプロイするWebアプリケーション

CloudFront Hosting ToolkitでデプロイするWebアプリケーションはNext.jsの以下サンプルを使用します。

https://nextjs.org/learn/dashboard-app

Chapter 5のNavigating Between Pagesまで完了させています。

また、ビルド時に画像を最適化するためにNext Export Optimize Imagesを使用しました。

https://next-export-optimize-images.vercel.app/docs/getting-started

用意したコードは以下GitHubリポジトリに保存しています。

https://github.com/non-97/nextjs-dashboard

ローカルで動作することを確認しましょう。

pnpm devで起動します。

$ pnpm dev

> @ dev /<作業ディレクトリ>
> next dev

info - [next-export-optimize-images]: Configuration loaded from `/<作業ディレクトリ>/export-images.config.js`.
info - [next-export-optimize-images]: Configuration loaded from `/<作業ディレクトリ>/export-images.config.js`.
  ▲ Next.js 15.0.0-rc.0
  - Local:        http://localhost:3000

 ✓ Starting...
 ✓ Ready in 1820ms

localhost:3000にアクセスすると以下のように表示されます。

27.localhost3000

/dashboard/customersにアクセスすると、以下のように表示されます。

28.http-::localhost-3000:dashboard:customers

アクセスした際、裏側では以下のようにコンパイルが走っています。

 ○ Compiling / ...
 ✓ Compiled / in 2.3s (573 modules)
 GET / 200 in 2517ms
 ✓ Compiled in 231ms (281 modules)
 ✓ Compiled /dashboard/customers in 332ms (571 modules)
 GET /dashboard/customers 200 in 443ms

L3 Constructで指定できるパラメーター

CloudFront Hosting ToolkitをAWS CDKのL3 Constructとして使用するにあたって、定義を確認します。

./node_modules/@aws/cloudfront-hosting-toolkit/lib/index.d.ts
export * from './hosting';
export * from './repository_connection';

./hosting./repository_connectionのみのようです。

それぞれの定義は以下のとおりです。

./node_modules/@aws/cloudfront-hosting-toolkit/lib/hosting.d.ts
import { Construct } from "constructs";
import { HostingConfiguration } from "../bin/cli/shared/types";
interface IParamProps {
    hostingConfiguration: HostingConfiguration;
    buildFilePath: string;
    cffSourceFilePath: string;
    connectionArn?: string;
    certificateArn?: string;
}
/**
 * Custom CDK Construct for hosting resources.
 *
 * This construct sets up hosting based on the provided configuration,
 * build file path, and optional connection and certificate ARNs.
 *
 * @param scope - The Construct scope in which this construct is defined.
 * @param id - The identifier for this construct within the scope.
 * @param params - Parameters for configuring hosting resources.
 *   - `configuration` (required): The IConfiguration object representing the hosting configuration.
 *   - `buildFilePath` (required): The path to the build file for the hosting resources.
 *   - `connectionArn` (optional): The ARN of the connection resource (if applicable).
 *   - `certificateArn` (optional): The ARN of the certificate resource (if applicable).
 */
export declare class Hosting extends Construct {
    constructor(scope: Construct, id: string, params: IParamProps);
}
export {};
./node_modules/@aws/cloudfront-hosting-toolkit/lib/repository_connection.d.ts
import { Construct } from "constructs";
import { HostingConfiguration } from "../bin/cli/shared/types";
/**
 * Custom CDK Construct for setting up a repository connection.
 *
 * This construct creates a connection to a hosting repository using the provided configuration.
 *
 * @param scope - The Construct scope in which this construct is defined.
 * @param id - The identifier for this construct within the scope.
 * @param params - Parameters for configuring the repository connection.
 *   - `repoUrl` (optional): The URL of the hosting repository.
 *   - `branchName` (optional): The name of the branch in the repository.
 *   - `framework` (optional): The framework used for hosting.
 *   - `s3bucket` (optional): The name of the Amazon S3 bucket for hosting content.
 *   - `s3path` (optional): The path within the S3 bucket where content is stored.
 *   - `domainName` (optional): The domain name associated with the hosting.
 *   - `hostedZoneId` (optional): The ID of the Route 53 hosted zone associated with the domain.
 */
export declare class RepositoryConnection extends Construct {
    readonly connectionArn: string;
    readonly repoUrl: string;
    constructor(scope: Construct, id: string, hostingConfiguration: HostingConfiguration);
}

指定できるパラメーターとしては多くないですね。自作したCloudFrontディストリビューションのConstructを渡すといったことは現時点ではできないようです。

それぞれで共通利用される定義は以下のとおりです。

./node_modules/@aws/cloudfront-hosting-toolkit/bin/cli/shared/types.d.ts
#!/usr/bin/env node
export interface CDKCommand {
    label: string;
    cmd: any;
}
export type CommonAttributes = {
    domainName?: string;
    hostedZoneId?: string;
};
export type HostingConfiguration = ({
    repoUrl: string;
    branchName: string;
    framework: string;
} & CommonAttributes) | ({
    s3bucket: string;
    s3path: string;
} & CommonAttributes);
export interface IChoice {
    title: string;
    value: string;
}
export interface IConnection {
    arn: string;
    name: string;
    region: string;
}
export interface IHosting {
    domain: string;
    source: string;
    type: string;
    pipeline: string;
}
export interface Dictionary<T> {
    [key: string]: T;
}
export interface PackageJson {
    dependencies?: Record<string, string>;
    devDependencies?: Record<string, string>;
    scripts?: Record<string, string>;
}
export declare const FrontendFramework: {
    REACT: string;
    VUE: string;
    ANGULAR: string;
    NEXT: string;
    BASIC: string;
};
export interface CNAMES {
    key: string;
    value: any;
}
export interface PromptItems {
    title: string;
    value: string;
}

デプロイメントの初期化

デプロイメントの初期化をして、CloudFront Functionsのコードなどを生成します。

$ npx cloudfront-hosting-toolkit init

--------------------- Static hosting configuration wizard : GitHub Source Code Repository Based -------------------

 To facilitate the deployment of the necessary infrastructure for website hosting, certain information is required.
 cloudfront-hosting-toolkit will aim to find as much relevant data as possible.

Collecting information about the GitHub repository from /<AWS CDK作業ディレクトリ>

✔ Please provide your GitHub repository URL … https://github.com/non-97/nextjs-dashboard.git
✔ What is the name of the branch you would like to use? Hit Enter to confirm or change the selection. … main

Collecting information about the frontend framework used to enable the provision of the appropriate build configuration.

✔ Which framework did you use for website construction? Press Enter to confirm or change the selection. › Next.js Framework

✔ Do you own a domain name that you would like to use? › Yes

✔ Please provide your domain name in the following formats: www.mydomainname.com or mydomainname.com ? … cf-hosting-toolkit.non-97.net
✔ Where is the authoritative DNS server of this domain? › Route 53 in this AWS Account

✔ Please type the hosted zone ID … Z05845813ALGIYU6AAH34

----------------------------------------------------
Here is the configuration that has been generated and saved to cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-config.json file.:
>       GitHub repository: https://github.com/non-97/nextjs-dashboard.git/main
>       Framework: nextjs
>       Domain name: cf-hosting-toolkit.non-97.net
>       Hosted zone ID: Z05845813ALGIYU6AAH34

--
>       Configuration file generated /<AWS CDK作業ディレクトリ>/cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-config.json
>       Build configuration generated /<AWS CDK作業ディレクトリ>/cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-build.yml
>       CloudFront Function source code generated /<AWS CDK作業ディレクトリ>/cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-cff.js

The initialization process has been completed. You may now execute 'cloudfront-hosting-toolkit deploy' to deploy the infrastructure.

生成されたファイルは以下のとおりです。

./cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-config.json
{
  "repoUrl": "https://github.com/non-97/nextjs-dashboard.git",
  "branchName": "main",
  "framework": "nextjs",
  "domainName": "cf-hosting-toolkit.non-97.net",
  "hostedZoneId": "Z05845813ALGIYU6AAH34"
}
./cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-build.yml
version: 0.2

phases:
  build:
    commands:
      - n install 18.17.0
      - npx npm install
      - npx next build
      - npx next export   
      - cd out
      - echo aws s3 cp ./ s3://$DEST_BUCKET_NAME/$CODEBUILD_RESOLVED_SOURCE_VERSION/ --recursive #don't change this line
      - aws s3 cp ./ s3://$DEST_BUCKET_NAME/$CODEBUILD_RESOLVED_SOURCE_VERSION/ --recursive #don't change this line
./cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-cff.js
import cf from 'cloudfront';

const kvsId = '__KVS_ID__';

// This fails if the key value store is not associated with the function
const kvsHandle = cf.kvs(kvsId);

function pointsToFile(uri) {
  return /\/[^/]+\.[^/]+$/.test(uri);
}
var rulePatterns = {
  "/$": "/index.html", // When URI ends with a '/', append 'index.html'
  "!file": ".html", // When URI doesn't point to a specific file and doesn't have a trailing slash, append '.html'
  "!file/": "/index.html",// When URI has a trailing slash and doesn't point to a specific file, append 'index.html'
};

// Function to determine rule and update the URI
async function updateURI(uri) {

  let pathToAdd = "";

  try {
    pathToAdd = await kvsHandle.get("path");
  } catch (err) {
      console.log(`No key 'path' present : ${err}`);
      return uri;
  }

  // Check for trailing slash and apply rule.
  if (uri.endsWith("/") && rulePatterns["/$"]) {
    return "/" + pathToAdd + uri.slice(0, -1) + rulePatterns["/$"];
  }

  // Check if URI doesn't point to a specific file.
  if (!pointsToFile(uri)) {
    // If URI doesn't have a trailing slash, apply rule.
    if (!uri.endsWith("/") && rulePatterns["!file"]) {
      return "/" + pathToAdd + uri + rulePatterns["!file"];
    }

    // If URI has a trailing slash, apply rule.
    if (uri.endsWith("/") && rulePatterns["!file/"]) {
      return "/" + pathToAdd + uri.slice(0, -1) + rulePatterns["!file/"];
    }
  }

  return "/" + pathToAdd + uri;
}

// Main CloudFront handler
async function handler(event) {
  var request = event.request;
  var uri = request.uri;

  //console.log("URI BEFORE: " + request.uri); // Uncomment if needed
  request.uri = await updateURI(uri); 
  //console.log("URI AFTER: " + request.uri); // Uncomment if needed

  return request;
}

なお、いずれもawslabs/cloudfront-hosting-toolkit/resources/build_config_templatesawslabs/cloudfront-hosting-toolkit/resources/cff_templatesに保存されています。

https://github.com/awslabs/cloudfront-hosting-toolkit/tree/main/resources

Node.js 20やpnpmを使用したかったのでbuildspac.ymlのみ修正しました。修正後は以下のとおりです。

./cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-build.yml
version: 0.2

env:
  variables:
    NEXT_EXPORT: true
phases:
  install:
    runtime-versions:
      nodejs: 20
  build:
    commands:
      - npm install -g pnpm
      - pnpm i
      - pnpm export
      - cd out
      - echo aws s3 cp ./ s3://$DEST_BUCKET_NAME/$CODEBUILD_RESOLVED_SOURCE_VERSION/ --recursive #don't change this line
      - aws s3 cp ./ s3://$DEST_BUCKET_NAME/$CODEBUILD_RESOLVED_SOURCE_VERSION/ --recursive #don't change this line

L3 Constructのプロパティでこれらのファイルを指定します。RepositoryConnection()のプロパティはTypeScriptファイル内で直接指定しました。

./lib/website-stack.ts
import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { Hosting, RepositoryConnection } from "@aws/cloudfront-hosting-toolkit";
import * as path from "path";

export class WebsiteStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const config = {
      repoUrl: "https://github.com/non-97/nextjs-dashboard.git",
      branchName: "main",
      framework: "nextjs",
      domainName: "cf-hosting-toolkit.non-97.net",
      hostedZoneId: "Z05845813ALGIYU6AAH34",
    };
    const repositoryConnection = new RepositoryConnection(
      this,
      "RepositoryConnection",
      config
    );

    const hosting = new Hosting(this, "Hosting", {
      hostingConfiguration: config,
      buildFilePath: path.join(
        __dirname,
        "../cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-build.yml"
      ),
      connectionArn: repositoryConnection.connectionArn,
      cffSourceFilePath: path.join(
        __dirname,
        "../cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-cff.js"
      ),
    });
  }
}

最新のコードは以下GitHubリポジトリに保存しています。

https://github.com/non-97/aws-cdk-cloudfront-hosting-toolkit

デプロイ

デプロイします。

npx cdk diffの結果は以下のとおりです。

$ npx cdk diff
Stack WebsiteStack
IAM Statement Changes
┌───┬──────────────────────────────────────────────────────┬────────┬──────────────────────────────────────────────────────┬──────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────┐
│   │ Resource                                             │ Effect │ Action                                               │ Principal                                            │ Condition                                               │
├───┼──────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
.
.
(中略)
.
.
Parameters
[+] Parameter BootstrapVersion BootstrapVersion: {"Type":"AWS::SSM::Parameter::Value<String>","Default":"/cdk-bootstrap/hnb659fds/version","Description":"Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"}

Mappings
[+] Mapping LatestNodeRuntimeMap LatestNodeRuntimeMap: {"af-south-1":{"value":"nodejs20.x"},"ap-east-1":{"value":"nodejs20.x"},"ap-northeast-1":{"value":"nodejs20.x"},"ap-northeast-2":{"value":"nodejs20.x"},"ap-northeast-3":{"value":"nodejs20.x"},"ap-south-1":{"value":"nodejs20.x"},"ap-south-2":{"value":"nodejs20.x"},"ap-southeast-1":{"value":"nodejs20.x"},"ap-southeast-2":{"value":"nodejs20.x"},"ap-southeast-3":{"value":"nodejs20.x"},"ap-southeast-4":{"value":"nodejs20.x"},"ca-central-1":{"value":"nodejs20.x"},"cn-north-1":{"value":"nodejs18.x"},"cn-northwest-1":{"value":"nodejs18.x"},"eu-central-1":{"value":"nodejs20.x"},"eu-central-2":{"value":"nodejs20.x"},"eu-north-1":{"value":"nodejs20.x"},"eu-south-1":{"value":"nodejs20.x"},"eu-south-2":{"value":"nodejs20.x"},"eu-west-1":{"value":"nodejs20.x"},"eu-west-2":{"value":"nodejs20.x"},"eu-west-3":{"value":"nodejs20.x"},"il-central-1":{"value":"nodejs20.x"},"me-central-1":{"value":"nodejs20.x"},"me-south-1":{"value":"nodejs20.x"},"sa-east-1":{"value":"nodejs20.x"},"us-east-1":{"value":"nodejs20.x"},"us-east-2":{"value":"nodejs20.x"},"us-west-1":{"value":"nodejs20.x"},"us-west-2":{"value":"nodejs20.x"}}

Conditions
[+] Condition CDKMetadata/Condition CDKMetadataAvailable: {"Fn::Or":[{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"af-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-northeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ap-southeast-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"ca-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"cn-northwest-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-north-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"eu-west-3"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"il-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-central-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"me-south-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"sa-east-1"]}]},{"Fn::Or":[{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-east-2"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-1"]},{"Fn::Equals":[{"Ref":"AWS::Region"},"us-west-2"]}]}]}

Resources
[+] AWS::CodeStarConnections::Connection RepositoryConnection/MyCfnConnectionnextjs-dashboard RepositoryConnectionMyCfnConnectionnextjsdashboard5BBC15B1
[+] AWS::SSM::Parameter RepositoryConnection/SSMConnectionArn RepositoryConnectionSSMConnectionArn1E74102B
[+] AWS::SSM::Parameter RepositoryConnection/SSMConnectionName RepositoryConnectionSSMConnectionNameAB648C27
[+] AWS::SSM::Parameter RepositoryConnection/SSMConnectionRegion RepositoryConnectionSSMConnectionRegion4029CE52
[+] AWS::CloudFront::KeyValueStore Hosting/UriStore HostingUriStoreC5D8CAEE
[+] AWS::CloudFront::Function Hosting/ChangeUri HostingChangeUri1A688977
[+] AWS::S3::Bucket Hosting/HostingInfrastructure/HostingBucket HostingHostingInfrastructureHostingBucketBB444313
[+] AWS::S3::BucketPolicy Hosting/HostingInfrastructure/HostingBucket/Policy HostingHostingInfrastructureHostingBucketPolicy905DD8E2
[+] AWS::CloudFront::ResponseHeadersPolicy Hosting/HostingInfrastructure/ResponseHeadersPolicy HostingHostingInfrastructureResponseHeadersPolicy630F57EC
[+] AWS::CloudFront::CachePolicy Hosting/HostingInfrastructure/DefaultCachePolicy HostingHostingInfrastructureDefaultCachePolicy266661D2
[+] AWS::CloudFront::CachePolicy Hosting/HostingInfrastructure/ImagesCachePolicy HostingHostingInfrastructureImagesCachePolicyFF009C20
[+] AWS::CloudFront::CachePolicy Hosting/HostingInfrastructure/staticAssetsCachePolicy HostingHostingInfrastructurestaticAssetsCachePolicy6B67ED12
[+] AWS::CloudFront::OriginAccessControl Hosting/HostingInfrastructure/OAC HostingHostingInfrastructureOACF290641F
[+] AWS::CloudFront::Distribution Hosting/HostingInfrastructure/Distribution HostingHostingInfrastructureDistribution2FE26CAE
[+] AWS::SSM::Parameter Hosting/HostingInfrastructure/SSMConnectionRegion HostingHostingInfrastructureSSMConnectionRegionA72A0577
[+] AWS::IAM::Role Hosting/PipelineInfrastructure/UpdateCFF/BasicLambdaRole HostingPipelineInfrastructureUpdateCFFBasicLambdaRole6E5EC6AE
[+] AWS::IAM::Policy Hosting/PipelineInfrastructure/UpdateCFF/BasicLambdaRole/DefaultPolicy HostingPipelineInfrastructureUpdateCFFBasicLambdaRoleDefaultPolicy58B6752A
[+] AWS::Lambda::LayerVersion Hosting/PipelineInfrastructure/UpdateCFF/AwsSdkLayer HostingPipelineInfrastructureUpdateCFFAwsSdkLayerEA1E386E
[+] AWS::Lambda::Function Hosting/PipelineInfrastructure/UpdateCFF/UpdateKvsFunction HostingPipelineInfrastructureUpdateCFFUpdateKvsFunction85F93F9D
[+] Custom::LogRetention Hosting/PipelineInfrastructure/UpdateCFF/UpdateKvsFunction/LogRetention HostingPipelineInfrastructureUpdateCFFUpdateKvsFunctionLogRetentionCCFB0174
[+] AWS::Lambda::Function Hosting/PipelineInfrastructure/UpdateCFF/DeleteOldDeployments HostingPipelineInfrastructureUpdateCFFDeleteOldDeployments1E3F3EDE
[+] Custom::LogRetention Hosting/PipelineInfrastructure/UpdateCFF/DeleteOldDeployments/LogRetention HostingPipelineInfrastructureUpdateCFFDeleteOldDeploymentsLogRetentionA1B2D84E
[+] AWS::Logs::LogGroup Hosting/PipelineInfrastructure/UpdateCFF/sfnLog HostingPipelineInfrastructureUpdateCFFsfnLog3A570214
[+] AWS::IAM::Role Hosting/PipelineInfrastructure/UpdateCFF/StaticHostingSF/Role HostingPipelineInfrastructureUpdateCFFStaticHostingSFRoleC8BF2D67
[+] AWS::IAM::Policy Hosting/PipelineInfrastructure/UpdateCFF/StaticHostingSF/Role/DefaultPolicy HostingPipelineInfrastructureUpdateCFFStaticHostingSFRoleDefaultPolicyE080F905
[+] AWS::StepFunctions::StateMachine Hosting/PipelineInfrastructure/UpdateCFF/StaticHostingSF HostingPipelineInfrastructureUpdateCFFStaticHostingSF1C3F6163
[+] AWS::Lambda::LayerVersion Hosting/PipelineInfrastructure/InitialPage/AwsCliLayer HostingPipelineInfrastructureInitialPageAwsCliLayer9A51F97C
[+] Custom::CDKBucketDeployment Hosting/PipelineInfrastructure/InitialPage/CustomResource HostingPipelineInfrastructureInitialPageCustomResource34A55526
[+] AWS::S3::Bucket Hosting/PipelineInfrastructure/ArtifactBucket HostingPipelineInfrastructureArtifactBucket000BDB60
[+] AWS::S3::BucketPolicy Hosting/PipelineInfrastructure/ArtifactBucket/Policy HostingPipelineInfrastructureArtifactBucketPolicy55FE49F0
[+] Custom::S3AutoDeleteObjects Hosting/PipelineInfrastructure/ArtifactBucket/AutoDeleteObjectsCustomResource HostingPipelineInfrastructureArtifactBucketAutoDeleteObjectsCustomResource8CA26117
[+] AWS::IAM::Role Hosting/PipelineInfrastructure/MyPipeline/Role HostingPipelineInfrastructureMyPipelineRoleB18081B5
[+] AWS::IAM::Policy Hosting/PipelineInfrastructure/MyPipeline/Role/DefaultPolicy HostingPipelineInfrastructureMyPipelineRoleDefaultPolicy804931F0
[+] AWS::CodePipeline::Pipeline Hosting/PipelineInfrastructure/MyPipeline HostingPipelineInfrastructureMyPipelineFC5FDDD0
[+] AWS::IAM::Role Hosting/PipelineInfrastructure/MyPipeline/Sources/GitHub-Source-nextjs-dashboard/CodePipelineActionRole HostingPipelineInfrastructureMyPipelineSourcesGitHubSourcenextjsdashboardCodePipelineActionRoleC4013751
[+] AWS::IAM::Policy Hosting/PipelineInfrastructure/MyPipeline/Sources/GitHub-Source-nextjs-dashboard/CodePipelineActionRole/DefaultPolicy HostingPipelineInfrastructureMyPipelineSourcesGitHubSourcenextjsdashboardCodePipelineActionRoleDefaultPolicy17547051
[+] AWS::IAM::Role Hosting/PipelineInfrastructure/MyPipeline/Build/Build-And-Copy-to-S3-nextjs-dashboard/CodePipelineActionRole HostingPipelineInfrastructureMyPipelineBuildBuildAndCopytoS3nextjsdashboardCodePipelineActionRole07513F10
[+] AWS::IAM::Policy Hosting/PipelineInfrastructure/MyPipeline/Build/Build-And-Copy-to-S3-nextjs-dashboard/CodePipelineActionRole/DefaultPolicy HostingPipelineInfrastructureMyPipelineBuildBuildAndCopytoS3nextjsdashboardCodePipelineActionRoleDefaultPolicy34DDAFB2
[+] AWS::IAM::Role Hosting/PipelineInfrastructure/MyPipeline/ChangeUri/Invoke/CodePipelineActionRole HostingPipelineInfrastructureMyPipelineChangeUriInvokeCodePipelineActionRole87BB8ADC
[+] AWS::IAM::Policy Hosting/PipelineInfrastructure/MyPipeline/ChangeUri/Invoke/CodePipelineActionRole/DefaultPolicy HostingPipelineInfrastructureMyPipelineChangeUriInvokeCodePipelineActionRoleDefaultPolicyE3E98FC9
[+] AWS::Logs::LogGroup Hosting/PipelineInfrastructure/MyLogGroup HostingPipelineInfrastructureMyLogGroupF5049128
[+] AWS::IAM::Role Hosting/PipelineInfrastructure/Project/Role HostingPipelineInfrastructureProjectRole0A86D4CE
[+] AWS::IAM::Policy Hosting/PipelineInfrastructure/Project/Role/DefaultPolicy HostingPipelineInfrastructureProjectRoleDefaultPolicyC3411297
[+] AWS::CodeBuild::Project Hosting/PipelineInfrastructure/Project HostingPipelineInfrastructureProject873982A1
[+] AWS::SSM::Parameter Hosting/PipelineInfrastructure/SSMPipelineName HostingPipelineInfrastructureSSMPipelineName8B6DD00A
[+] AWS::IAM::Role LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRole9741ECFB
[+] AWS::IAM::Policy LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a/ServiceRole/DefaultPolicy LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aServiceRoleDefaultPolicyADDA7DEB
[+] AWS::Lambda::Function LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8a LogRetentionaae0aa3c5b4d4f87b02d85b201efdd8aFD4BFC8A
[+] AWS::IAM::Role Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265
[+] AWS::IAM::Policy Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF
[+] AWS::Lambda::Function Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536
[+] AWS::IAM::Role Custom::S3AutoDeleteObjectsCustomResourceProvider/Role CustomS3AutoDeleteObjectsCustomResourceProviderRole3B1BD092
[+] AWS::Lambda::Function Custom::S3AutoDeleteObjectsCustomResourceProvider/Handler CustomS3AutoDeleteObjectsCustomResourceProviderHandler9D90184F

Outputs
[+] Output RepositoryConnection/ConnectionArn RepositoryConnectionConnectionArn087CB8E2: {"Value":{"Fn::GetAtt":["RepositoryConnectionMyCfnConnectionnextjsdashboard5BBC15B1","ConnectionArn"]}}
[+] Output RepositoryConnection/ConnectionName RepositoryConnectionConnectionNameFD2EABB9: {"Value":"nextjs-dashboard-main-non-97"}
[+] Output RepositoryConnection/HostingRegion RepositoryConnectionHostingRegion1B4A18AD: {"Value":{"Ref":"AWS::Region"}}
[+] Output Hosting/HostingInfrastructure/DomainName HostingHostingInfrastructureDomainNameF9962E64: {"Value":{"Fn::Join":["",["https://",{"Fn::GetAtt":["HostingHostingInfrastructureDistribution2FE26CAE","DomainName"]}]]}}
[+] Output Hosting/PipelineInfrastructure/PipelineName HostingPipelineInfrastructurePipelineNameC86AA64C: {"Value":{"Ref":"HostingPipelineInfrastructureMyPipelineFC5FDDD0"}}

Other Changes
[+] Unknown Rules: {"CheckBootstrapVersion":{"Assertions":[{"Assert":{"Fn::Not":[{"Fn::Contains":[["1","2","3","4","5"],{"Ref":"BootstrapVersion"}]}]},"AssertDescription":"CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."}]}}

✨  Number of stacks with differences: 1

大量ですね。CLIの場合は、スタックがいくつかに分かれていますが、L3 Constructの場合は一つのスタックにまとめて定義することが可能です。

npx cdk deployでデプロイします。

$ npx cdk deploy

✨  Synthesis time: 16.69s

This deployment will make potentially sensitive changes according to your current security approval level (--require-approval broadening).
Please confirm you intend to make the following modifications:

IAM Statement Changes
┌───┬──────────────────────────────────────────────────────┬────────┬──────────────────────────────────────────────────────┬──────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────┐
│   │ Resource                                             │ Effect │ Action                                               │ Principal                                            │ Condition                                               │
├───┼──────────────────────────────────────────────────────┼────────┼──────────────────────────────────────────────────────┼──────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────┤
│ + │ ${Custom::CDKBucketDeployment8693BB64968944B69AAFB0C │ Allow  │ sts:AssumeRole                                       │ Service:lambda.amazonaws.com                         │                                                         │
.
.
(中略)
.
.
Do you wish to deploy these changes (y/n)? y
WebsiteStack: deploying... [1/1]
WebsiteStack: creating CloudFormation changeset...

 ✅  WebsiteStack

✨  Deployment time: 291.2s

Outputs:
WebsiteStack.HostingHostingInfrastructureDomainNameF9962E64 = https://d2uctbvmq5rfn4.cloudfront.net
WebsiteStack.HostingPipelineInfrastructurePipelineNameC86AA64C = non-97-nextjs-dashboard
WebsiteStack.RepositoryConnectionConnectionArn087CB8E2 = arn:aws:codestar-connections:us-east-1:<AWSアカウントID>:connection/77713fc5-903b-4df7-8f50-9e87bda64f20
WebsiteStack.RepositoryConnectionConnectionNameFD2EABB9 = nextjs-dashboard-main-non-97
WebsiteStack.RepositoryConnectionHostingRegion1B4A18AD = us-east-1
Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/WebsiteStack/8b7347c0-384d-11ef-a7d6-0affcdd81315

✨  Total time: 307.89s

5分ほどで完了しました。

CodePipelineを確認すると、Sourcesステージで失敗していました。

8.non-97-nextjs-sampleが失敗していることを確認

ChangeUriまで完了していないため、CloudFront FunctionsのKeyValueStoresは空になっています。

14.WebsiteStack-us-east-1

Sourcesステージで失敗したのはGitHubリポジトリとの連携が完了していないためです。

9.Connection nextjs-dashboard-main-non-97 is not available

連携状態を確認すると、ステータスが保留中になっています。

前回の記事を参考にGitHubと連携をします。(GitHub側の操作は割愛)

11.GitHibに接続する

12.ステータスが利用可能に

GitHubリポジトリとの連携が完了したのち、先ほど失敗したパイプラインで変更をリリースします。

13.変更をリリースする

次はChangeUriで失敗していました。

15.ChangeUriで失敗

StepFunctionsのステートマシンを確認すると、前回の記事同様AWS CRTのバイナリがないというエラーが出力されていました。

16.AWS CRT binary not present in any of the following locations

これは修正が必要そうです。

なお、Buildステージは正常に終了しており、S3バケットにビルドした結果が保存されていました。

17.S3バケットに展開されていることを確認

ただし、ChangeUriステージが完了していないため、アクセスをしてもデフォルトのコンテンツを向いたままです。

$ curl https://d2uctbvmq5rfn4.cloudfront.net
<!DOCTYPE html>
<html>
<head>
    <link rel="shortcut icon" href="#" />
    <meta http-equiv="refresh" content="10">
    <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
    <title>Your website is currently being deployed</title>
    <style type="text/css">
      body { text-align: center; padding: 150px; }
      h1 { font-size: 40px; }
      body { font: 20px Helvetica, sans-serif; color: #333; }
      #article { display: block; text-align: center; width: 1000px; margin: 0 auto; }
      a { color: #dc8100; text-decoration: none; }
      a:hover { color: #333; text-decoration: none; }
    </style>
</head>
<body>
<div id="article">
<h1>Deployment status</h1>
<div>
  <p>Please note that your are currently seeing this screen because this is the first deployment of the website. So, just take it easy and unwind, the page will automatically refresh on its own.</p>
</div>
</div>
</html>

Lambda Layerでバンドルするパッケージを手動でインストールして再トライ

Lambda Layerでバンドルするパッケージを手動でインストールして再トライします。ちなみに、CloudFront Hosting Toolkitをグローバルインストールしても特に結果は変わりませんでした。

./node_modules/@aws/cloudfront-hosting-toolkit/lambda/layers/aws_sdk/nodejs/を確認すると、エラーメッセージに記載されている./aws-crt/dist/binは存在しません。

$ cd node_modules/@aws/cloudfront-hosting-toolkit/lambda/layers/aws_sdk/nodejs/
$  ls -l
total 192
drwxr-xr-x@ 74 <OSユーザー名>  staff   2368  7  3 07:20 node_modules/
-rw-r--r--@  1 <OSユーザー名>  staff  92858  7  3 07:20 package-lock.json
-rw-r--r--@  1 <OSユーザー名>  staff    540  7  3 07:20 package.json

$ cat package.json
{
    "name": "AWS_SDK_Layer",
    "version": "1.0.0",
    "description": "Latest AWS SDK libs",
    "main": "",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "dependencies": {
        "@aws-sdk/client-cloudfront-keyvaluestore": "^3.511.0",
        "@aws-sdk/signature-v4-crt": "^3.511.0",
        "@aws-sdk/signature-v4-multi-region": "^3.511.0"
    },
    "author": {
        "name": "Amazon Web Services",
        "url": "https://aws.amazon.com/solutions"
    },
    "license": "Apache-2.0"
}

$ ls -l node_modules/aws-crt/dist/
total 8
drwxr-xr-x@ 18 <OSユーザー名>  staff   576  7  3 07:20 common/
-rw-r--r--@  1 <OSユーザー名>  staff  2948  7  3 07:20 index.js
drwxr-xr-x@ 19 <OSユーザー名>  staff   608  7  3 07:20 native/

手動でインストールします。

$ npm ci

added 159 packages, and audited 160 packages in 5s

10 packages are looking for funding
  run `npm fund` for details

1 high severity vulnerability

To address all issues, run:
  npm audit fix

Run `npm audit` for details.

$ ls -l node_modules/aws-crt/dist/
total 24
drwxr-xr-x@  8 <OSユーザー名>  staff   256  7  3 07:43 bin/
drwxr-xr-x@ 50 <OSユーザー名>  staff  1600  7  3 07:43 common/
-rw-r--r--@  1 <OSユーザー名>  staff   809  7  3 07:43 index.d.ts
-rw-r--r--@  1 <OSユーザー名>  staff  2948  7  3 07:43 index.js
-rw-r--r--@  1 <OSユーザー名>  staff   563  7  3 07:43 index.js.map
drwxr-xr-x@ 53 <OSユーザー名>  staff  1696  7  3 07:43 native/

$ ls -l node_modules/aws-crt/dist/bin/
total 0
drwxr-xr-x@ 3 <OSユーザー名>  staff  96  7  3 07:43 darwin-arm64-cruntime/
drwxr-xr-x@ 3 <OSユーザー名>  staff  96  7  3 07:43 darwin-x64-cruntime/
drwxr-xr-x@ 3 <OSユーザー名>  staff  96  7  3 07:43 linux-arm64-glibc/
drwxr-xr-x@ 3 <OSユーザー名>  staff  96  7  3 07:43 linux-x64-glibc/
drwxr-xr-x@ 3 <OSユーザー名>  staff  96  7  3 07:43 linux-x64-musl/
drwxr-xr-x@ 3 <OSユーザー名>  staff  96  7  3 07:43 win32-x64-cruntime/

お目当てのbinディレクトリが生成されました。

再度デプロイしましょう。

$ npx cdk diff
Stack WebsiteStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Resources
[~] AWS::Lambda::LayerVersion Hosting/PipelineInfrastructure/UpdateCFF/AwsSdkLayer HostingPipelineInfrastructureUpdateCFFAwsSdkLayerEA1E386E replace
 └─ [~] Content (requires replacement)
     └─ [~] .S3Key:
         ├─ [-] 8573a0a228ac578823a4d559179cafa3c8b436a4cd8b5fbe88e0c77ef9b6bc49.zip
         └─ [+] 9bf43224bde174dac822dcdf2c16fc5861466e13a99cca32ad38c82ce9f21f9b.zip

✨  Number of stacks with differences: 1

$ npx cdk deploy

✨  Synthesis time: 7.75s

WebsiteStack:  start: Building 9bf43224bde174dac822dcdf2c16fc5861466e13a99cca32ad38c82ce9f21f9b:current_account-current_region
WebsiteStack:  success: Built 9bf43224bde174dac822dcdf2c16fc5861466e13a99cca32ad38c82ce9f21f9b:current_account-current_region
WebsiteStack:  start: Building f90fcc9cd945165d7dab242b9e1f577cfae1ff02b15c294f9a09da086ace5f82:current_account-current_region
WebsiteStack:  success: Built f90fcc9cd945165d7dab242b9e1f577cfae1ff02b15c294f9a09da086ace5f82:current_account-current_region
WebsiteStack:  start: Publishing 9bf43224bde174dac822dcdf2c16fc5861466e13a99cca32ad38c82ce9f21f9b:current_account-current_region
WebsiteStack:  start: Publishing f90fcc9cd945165d7dab242b9e1f577cfae1ff02b15c294f9a09da086ace5f82:current_account-current_region
WebsiteStack:  success: Published f90fcc9cd945165d7dab242b9e1f577cfae1ff02b15c294f9a09da086ace5f82:current_account-current_region
WebsiteStack:  success: Published 9bf43224bde174dac822dcdf2c16fc5861466e13a99cca32ad38c82ce9f21f9b:current_account-current_region
WebsiteStack: deploying... [1/1]
WebsiteStack: creating CloudFormation changeset...

 ✅  WebsiteStack

✨  Deployment time: 72.17s

Outputs:
WebsiteStack.HostingHostingInfrastructureDomainNameF9962E64 = https://d2uctbvmq5rfn4.cloudfront.net
WebsiteStack.HostingPipelineInfrastructurePipelineNameC86AA64C = non-97-nextjs-dashboard
WebsiteStack.RepositoryConnectionConnectionArn087CB8E2 = arn:aws:codestar-connections:us-east-1:<AWSアカウントID>:connection/77713fc5-903b-4df7-8f50-9e87bda64f20
WebsiteStack.RepositoryConnectionConnectionNameFD2EABB9 = nextjs-dashboard-main-non-97
WebsiteStack.RepositoryConnectionHostingRegion1B4A18AD = us-east-1
Stack ARN:
arn:aws:cloudformation:us-east-1:<AWSアカウントID>:stack/WebsiteStack/8b7347c0-384d-11ef-a7d6-0affcdd81315

✨  Total time: 79.92s

Lambda Layerが更新されたようですね。

ChangeUriステージを再試行すると、正常に実行が完了しました。

18.実行完了

ステートマシンも以下のように成功しています。

19.ステートマシンの実行結果確認

実際にアクセスしてみましょう。

トップページにアクセスすると画像が表示されていません。

20.画像が表示されていない

画像を含んでいないページについては特に問題ありません

21.ダッシュボードは表示される

next/image を next-export-optimize-images/image に置換して再トライ

原因はNext Export Optimize Imagesを使っているにも関わらず、app/page.tsxnext/imageを使用していたためです。

next/imagenext-export-optimize-images/imageに置換して再トライします。

以下のように置換します。

app/page.tsx
- import Image from 'next/image';
+ import Image from 'next-export-optimize-images/image';

置換後、ローカルでビルドをして、正常に画像が表示されることを確認します。

以下のようにビルドをして、ローカルのWebサーバーを起動させます。

$ pnpm export

> @ export /<作業ディレクトリ>
> NEXT_EXPORT=true next build && next-export-optimize-images

info - [next-export-optimize-images]: Configuration loaded from `/<作業ディレクトリ>/export-images.config.js`.
  ▲ Next.js 15.0.0-rc.0

   Creating an optimized production build ...
 ✓ Compiled successfully
 ✓ Linting and checking validity of types
 ✓ Collecting page data
 ✓ Generating static pages (7/7)
 ✓ Collecting build traces
 ✓ Finalizing page optimization

Route (app)                              Size     First Load JS
┌ ○ /                                    6.53 kB         104 kB
├ ○ /_not-found                          896 B          89.9 kB
├ ○ /dashboard                           142 B          89.1 kB
├ ○ /dashboard/customers                 142 B          89.1 kB
└ ○ /dashboard/invoices                  142 B          89.1 kB
+ First Load JS shared by all            89 kB
  ├ chunks/539-816c8e2f3535b557.js       35.6 kB
  ├ chunks/8e36bf0b-94faa33a74e31594.js  51.5 kB
  └ other shared chunks (total)          1.9 kB

○  (Static)  prerendered as static content

next-export-optimize-images: Optimize images.
info - [next-export-optimize-images]: Configuration loaded from `/<作業ディレクトリ>/export-images.config.js`.

- Collect images in public directory -

- Image Optimization -
Optimization progress |████████████████████████████████████████| 100% || 288/288 Chunks
Cache assets: 288, NonCache assets: 0, Error assets: 0

Successful optimization!

$ npx http-server out
Starting up http-server, serving out

http-server version: 14.1.1

http-server settings:
CORS: disabled
Cache: 3600 seconds
Connection Timeout: 120 seconds
Directory Listings: visible
AutoIndex: visible
Serve GZIP Files: false
Serve Brotli Files: false
Default File Extension: none

Available on:
  http://127.0.0.1:8080
  http://192.168.3.19:8080
Hit CTRL-C to stop the server

アクセスすると、正常に画像が表示されることを確認できました。

22.localhost8080では画像が表示されることを確認

修正したコードをGitHubリポジトリにPushします。

すると、CodePipelineが動き始め、正常に実行が完了しました。

24.パイプラインが通ったことを確認

アクセスをすると、今度は正常に画像が表示されました。

25.CloudFrontでも表示されたことを確認

ウィンドウの幅を変更をして、表示される画像が切り替わることも確認できました。

26.使用する画像が変動することを確認

カスタムドメインの設定

ここで皆さんお気づきではないでしょうか。

カスタムドメインが設定されていないことに。

CLIの場合はカスタムドメインとRoute 53 Public Hosted ZoneのIDを渡してあげると、ACMで証明書を発行し、CloudFrontディストリビューションでカスタムドメインの設定をしてくれました。

L3 Constructの場合は、そうではないようです。ACMで証明書の発行はされておらず、CloudFrontディストリビューションの代替ドメイン名も設定されていませんでした。

29.カスタムドメインが設定されていない

自身でACMの証明書を発行し、L3 Constructのパラメーターとして渡してあげます。

ACMでパブリック証明書をリクエストします。

33.パブリック証明書をリクエスト

ここでZone Apexだけでなく、サブドメインwwwも追加したのは以下のエラーを防ぐためです。

13:18:32 | UPDATE_FAILED        | AWS::CloudFront::Distribution          | HostingHostingInfr...stribution2FE26CAE
Resource handler returned message: "Invalid request provided: The certificate that is attached to your distribution doesn't cover the alternate domain name (CNAME) that you're trying to add. For more details, see: https://docs.aws.amazon.co
m/AmazonCloudFront/latest/DeveloperGuide/CNAMEs.html#alternate-domain-names-requirements (Service: CloudFront, Status Code: 400, Request ID: 3f6f6e0c-e0ee-4dad-b79d-d2161f9560b4)" (RequestToken: b381ec3d-3b3d-100a-f979-a61071ff7cad, Handler
ErrorCode: InvalidRequest)

Route 53に検証用のCNAMEレコードを追加します。

34.Amazon Route 53 で DNS レコードを作成

1分ほど待つと証明書の検証が完了します。

35.証明書の検証が完了

証明書のARNをHosting()certificateArnに指定します。

./lib/website-stack.ts
    const hosting = new Hosting(this, "Hosting", {
      hostingConfiguration: config,
      buildFilePath: path.join(
        __dirname,
        "../cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-build.yml"
      ),
      connectionArn: repositoryConnection.connectionArn,
      cffSourceFilePath: path.join(
        __dirname,
        "../cloudfront-hosting-toolkit/cloudfront-hosting-toolkit-cff.js"
      ),
      certificateArn:
        "arn:aws:acm:us-east-1:<AWSアカウントID>:certificate/0722854d-637f-4f66-a3b0-3a0236a13255",
    });

npx cdk diffを叩いて、差分を確認します。

$ npx cdk diff
Stack WebsiteStack
Hold on while we create a read-only change set to get a diff with accurate replacement information (use --no-change-set to use a less accurate but faster template-only diff)
Resources
[~] AWS::CloudFront::Distribution Hosting/HostingInfrastructure/Distribution HostingHostingInfrastructureDistribution2FE26CAE
 └─ [~] DistributionConfig
     ├─ [+] Added: .Aliases
     └─ [+] Added: .ViewerCertificate

✨  Number of stacks with differences: 1

CloudFrontディストリビューションに代替ドメインと証明書の設定が行われるようです。

Route 53 Public HostedにCloudFrontディストリビューションのALIASレコードを追加するまでは行ってくれなさそうなので注意ですね。

npx cdk deployでデプロイ後、CloudFrontディストリビューションを確認すると、代替ドメイン名およびカスタム SSL 証明書が設定されたことを確認できました。

一方で、まだALIASレコードは作成されていないため、名前解決はできません。

$ dig cf-hosting-toolkit.non-97.net +short

手動でALIASレコードを登録します。

37.CloudFrontディストリビューションのALIASレコードを追加する

登録後、名前解決および、カスタムドメインでアクセスできることを確認します。

$ dig cf-hosting-toolkit.non-97.net +short
18.65.216.62
18.65.216.60
18.65.216.92
18.65.216.125

$ curl https://cf-hosting-toolkit.non-97.net -I
HTTP/2 200
date: Wed, 03 Jul 2024 04:58:57 GMT
content-type: text/html
content-length: 10932
last-modified: Wed, 03 Jul 2024 00:34:44 GMT
etag: "60de3a39bfa79ada05241daa142400bf"
x-amz-server-side-encryption: AES256
accept-ranges: bytes
server: AmazonS3
vary: Accept-Encoding
x-cache: Miss from cloudfront
via: 1.1 2944fb08ed200b542920ceadbff2083e.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT57-P4
alt-svc: h3=":443"; ma=86400
x-amz-cf-id: DTtOvKlk98NxDeAuUotEHC_Sjz4NT8bEIIqUpdur5Q7jO03tHG17qA==
x-xss-protection: 1; mode=block
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
x-content-type-options: nosniff
strict-transport-security: max-age=31536000; includeSubDomains

念の為ブラウザからも確認しましたが、問題なくアクセスできました。

38.https___cf-hosting-toolkit_non-97_net_

5分ほどでフロントエンドのホスティングとCI/CDパイプラインを用意することができる

CloudFront Hosting ToolkitをAWS CDKのL3 Constructとして使ってみました。

多少のケアは必要ですが、5分ほどでフロントエンドのホスティングとCI/CDパイプラインを用意することができるのは非常にありがたいですね。

CloudFrontディストリビューションの細かいカスタマイズはできないので、そこは手動で行うことになるのは注意しましょう。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.